package com.dubbo.homework;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

public class MethodPercentageManager implements Runnable{


    //多久时间需要上传
    private final int totalSeconds;
    // 定义一个容器存储刚刚好需要上报的数据
    private final MethodSecond[] methodSeconds;

    public MethodPercentageManager(int totalSeconds) {
        this.totalSeconds = totalSeconds;
        methodSeconds = new MethodSecond[totalSeconds];
        for(int i = 0; i < methodSeconds.length ; i++){
            methodSeconds[i] = new MethodSecond();
        }

    }


    public static MethodPercentageManager create(int totalSeconds , int reportPerTime){
        MethodPercentageManager methodPercentageManager = new MethodPercentageManager(totalSeconds);
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                methodPercentageManager,
                5,
                TimeUnit.SECONDS.toMillis(reportPerTime),
                TimeUnit.MILLISECONDS
        );
        return methodPercentageManager;
    }
    public void increment(String methodName , long useTime ,long currentTime){
        MethodSecond methodSecond = methodSeconds[(int) (currentTime / 1000 % totalSeconds)];
        methodSecond.increment(methodName,useTime,currentTime);
    }

    @Override
    public void run() {
        final long reportStartTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(totalSeconds);
        List<MethodSecond> collect = Arrays.asList(methodSeconds)
                .stream()
                .filter((methodSecond) -> methodSecond.isSupport(reportStartTime))
                .collect(Collectors.toList());
        if (collect.isEmpty()) {
            return;
        }

        // 进行上报数据
        final Map<String, Result> reportData = doreport(collect, 90, 99);

        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String dateStr = sdf.format(date) + ":";

        for (Map.Entry<String, Result> entry : reportData.entrySet()) {
            final Result result = entry.getValue();
            System.out.println(dateStr + entry.getKey() + "中的TP90:" + result.getPercentage().get(90) + "毫秒, TP99:" + result.getPercentage().get(99) + "毫秒, " +
                    "总计调用次数:" + result.getTotalCount());
        }

        System.out.println("---------------------------------------------------------------------");
    }



    public Map<String,Result> doreport(List<MethodSecond> timeBuckets,Integer...tpValue){
        if(null == tpValue || tpValue.length <= 0){
            return Collections.emptyMap();
        }
        final Map<String , SortedMap<Long,Long>> dataMap = new HashMap<>();
        for (MethodSecond timeBucket : timeBuckets) {
            for (Map.Entry<String, ConcurrentHashMap<Long, AtomicLong>> entry : timeBucket.countMap.entrySet()) {
                SortedMap<Long, Long> longLongSortedMap = dataMap.computeIfAbsent(entry.getKey(), (m) -> new TreeMap<>(Long::compareTo));
                for (Map.Entry<Long, AtomicLong> longAtomicLongEntry : entry.getValue().entrySet()) {
                    Long count = longLongSortedMap.getOrDefault(longAtomicLongEntry.getKey(), 0L);
                    count += longAtomicLongEntry.getValue().get();
                    longLongSortedMap.put(longAtomicLongEntry.getKey(),count);
                }
            }
        }
        final HashMap<String, Result> result = new HashMap<>();
        for (Map.Entry<String, SortedMap<Long, Long>> stringSortedMapEntry : dataMap.entrySet()) {
            String methodName = stringSortedMapEntry.getKey();
            SortedMap<Long, Long> value = stringSortedMapEntry.getValue();

            final HashMap<Integer,Long> tpValues = new HashMap<>();
            final long sum = value.values().stream().mapToLong(t -> t).sum();
            for (Integer integer : tpValue) {
                tpValues.put(integer,getTpValue(value,sum,integer));
            }
            final Result methodResult = new Result();
            methodResult.setMethodName(methodName);
            methodResult.setPercentage(tpValues);
            methodResult.setTotalCount(sum);
            result.put(methodName,methodResult);
        }
        return result;
    }

    public Long getTpValue(SortedMap<Long, Long> value,Long sum,Integer integer){
        int roof = Math.round(sum * integer * 1.0f / 100);
        int count = 0;
        Long v = 0L;
        for (Map.Entry<Long, Long> longLongEntry : value.entrySet()) {
            count += longLongEntry.getValue();
            if(count >= roof){
                return longLongEntry.getKey();
            }
        }
        return v;
    }


    class MethodSecond{

        private final ConcurrentHashMap<String,ConcurrentHashMap<Long, AtomicLong>> countMap = new ConcurrentHashMap<>();

        private volatile long lastIncrementTime = -1;

        public void increment(String methodName , long useTime ,long currentTime){
            if (lastIncrementTime > 0 && currentTime - lastIncrementTime > 1000) {
                synchronized (this) {
                    if (currentTime - lastIncrementTime > 1000 && countMap.size() > 0) {
                        countMap.clear();
                        lastIncrementTime = currentTime;
                    }
                }
            }
            ConcurrentHashMap<Long, AtomicLong> timeWithCount = countMap.computeIfAbsent(methodName, (m) -> new ConcurrentHashMap<>());
            final AtomicLong atomicLong = timeWithCount.computeIfAbsent(useTime, (t) -> new AtomicLong());
            atomicLong.incrementAndGet();
            lastIncrementTime = currentTime;
        }

        public boolean isSupport(long supportTime){
            return lastIncrementTime > supportTime;
        }
    }
}
